/*
 * Controlador.cpp
 *
 */

#include "Controlador.h"

/**
 * Constructor del controlador.
 * Recibe los semaforos para sincronizarse con el ascensor, el pipe
 * para recibir llamadas de los pisos y el pid del ascensor para enviarle
 * seniales.
 */
Controlador::Controlador( Semaforos semaforos, Pipe* pipeLlamadas, pid_t pidAscensor )
	: semaforos ( semaforos ),
	  pipeLlamadas ( pipeLlamadas ),
	  pidAscensor ( pidAscensor ),
	  memComp ( MemoriaCompartida<Llamada>() ){

}

/**
 * Metodo que inicializa los mecanismos de concurrencia que usa el
 * controlador.
 * Crea la memoria compartida con el ascensor y registra el handler
 * de seniales.
 */
int Controlador::inicializar(){
	int retorno = -1;

	if ( this->memComp.crear( (char*) ARCHIVO_MEM_COMP, ID_MEM_COMP ) != SHM_OK ) {
		Log::getInstance()->loguear("Controlador: Error al crear la memoria compartida.");

	} else {
		SignalHandler::getInstance()->registrarHandler( SIGABRT, this );
		this->bloquearSeniales();
		Log::getInstance()->loguear("Controlador: creado.");
		retorno = 0;
	}

	return retorno;
}

/**
 * Metodo que setea que las seniales SIGINT y SIGTERM se bloquean.
 * El objetivo es que el controlador no salga del loop del pipe para
 * manejar estas seniales.
 * Las seniales SIGINT y SIGTERM las agarra el terminador para finalizar
 * la ejecucion correctamente.
 */
void Controlador::bloquearSeniales(){
	sigset_t set;
	sigemptyset( &set );
	sigaddset( &set, SIGINT );
	sigaddset( &set, SIGTERM );
	sigprocmask ( SIG_BLOCK, &set, NULL );
}

/**
 * Destructor del controlador.
 * Elimina el handler de seniales, cierra el pipe de llamadas y elimina
 * la memoria compartida con el ascensor.
 */
Controlador::~Controlador() {
	SignalHandler::getInstance()->removerHandler( SIGABRT );
	SignalHandler::destruir();

	this->pipeLlamadas->cerrar();
	this->memComp.liberar();

	Log::getInstance()->loguear("Controlador: destruido.");
}
/**
 * Handler de seniales:
 * SIGABRT: Senial que envia el terminador en caso de que haya errores.
 * 		Se finaliza la ejecucion inmediatamente liberando los recursos.
 */
int Controlador::handleSignal( int signum ){
	if ( signum == SIGABRT ){
		Log::getInstance()->loguear("Controlador: me llego SIGABRT. Finalizo ante errores.");
		this->~Controlador();
		exit( -1 );
	}
	return 0;
}

/**
 * Metodo que se ejecuta de forma indefinida hasta que todos los pisos
 * hayan cerrado el pipe de llamadas.
 * Permanece bloqueado leyendo el pipe, esperando a que los pisos envien
 * llamadas al ascensor.
 */
void Controlador::recibirLlamadas(){
	Llamada llamada;
	while ( this->pipeLlamadas->leer( &llamada, sizeof(Llamada) ) > 0 ) {
		Log::getInstance()->loguear("Controlador: recibi llamada del piso " + intToString( llamada.getPiso() ) + " desde el pipe.");
		this->avisarAscensor( llamada );

	}
	Log::getInstance()->loguear("Controlador: no se reciben mas llamadas al ascensor.");
}

/**
 * Metodo que espera el semaforo de escritura, escribe una llamada en
 * la memoria compartida, y le envia una senial al ascensor avisandole
 * que puede leer la llamada.
 */
void Controlador::avisarAscensor(Llamada llamada){
	this->semaforos.escritura->p();
	this->memComp.escribir(llamada);

	kill( pidAscensor, SIGUSR1 );
}

